#pragma once

#include <vector>
#include <memory>
#include "llvm/IR/Value.h"
#include "type.h"

// prog : (expr? ";")*
// expr : term (("+" | "-") term)* ;
// term : factor (("*" | "/") factor)* ;
// factor : number | "(" expr ")" ;
// number: ([0-9])+ ;

enum class OpCode
{
    add,
    sub,
    mul,
    div
};

// 进行声明
class Program;
class Expr;
class VariableDecl;
class VariableAccessExpr;
class BinaryExpr;
class AssignExpr;
class NumberExpr;

// 访问者模式
class Visitor
{
public:
    virtual ~Visitor() {}
    virtual llvm::Value *VisitProgram(Program *p) = 0;
    virtual llvm::Value *VisitVariableDecl(VariableDecl* decl) = 0;
    virtual llvm::Value *VisitVariableAccessExpr(VariableAccessExpr* varaccExpr) = 0;
    virtual llvm::Value *VisitAssignExpr(AssignExpr* assignExpr) = 0;
    virtual llvm::Value *VisitBinaryExpr(BinaryExpr *binaryExpr) = 0;
    virtual llvm::Value *VisitNumberExpr(NumberExpr *factorExpr) = 0;
};

// 语法树的公共节点
class ASTNode 
{
public:
    enum Kind
    {
        ND_VariableDecl,
        ND_BinaryExpr,
        ND_NumberExpr,
        ND_VariableAccessExpr,
        ND_AssignExpr
    };

private:
    const Kind kind;
public:
    ASTNode(Kind kind) : kind(kind) {}
    virtual ~ASTNode() {}
    virtual llvm::Value *Accept(Visitor *v) { return nullptr; } // 通过虚函数的特性完成分发的功能
    const Kind getKind() const { return kind; }

public:
    CType *type;
};

// 变量声明节点
class VariableDecl : public ASTNode 
{
public:
    VariableDecl() : ASTNode(ND_VariableDecl) {}

    llvm::Value *Accept(Visitor *v) override
    {
        return v->VisitVariableDecl(this);
    }

    static bool classof(const ASTNode *node) // 判断是否能进行指针的强转
    {
        return node->getKind() == ND_VariableDecl;
    }
public:
    llvm::StringRef name;
};

// 二元表达式节点
class BinaryExpr : public ASTNode
{
public:
    BinaryExpr() : ASTNode(ND_BinaryExpr) {}

    llvm::Value *Accept(Visitor *v) override
    {
        return v->VisitBinaryExpr(this);
    }

    static bool classof(const ASTNode *node) // 判断是否能进行指针的强转
    {
        return node->getKind() == ND_BinaryExpr;
    }
public:
    OpCode op;
    std::shared_ptr<ASTNode> left;
    std::shared_ptr<ASTNode> right;
};

// 赋值表达式节点
class AssignExpr : public ASTNode
{
public:
    AssignExpr() : ASTNode(ND_AssignExpr) {}

    llvm::Value *Accept(Visitor *v) override
    {
        return v->VisitAssignExpr(this);
    }

    static bool classof(const ASTNode *node) // 判断是否能进行指针的强转
    {
        return node->getKind() == ND_AssignExpr;
    }
public:
    std::shared_ptr<ASTNode> left;
    std::shared_ptr<ASTNode> right;
};

// 数字表达式节点
class NumberExpr : public ASTNode
{
public:
    NumberExpr() : ASTNode(ND_NumberExpr) {}

    llvm::Value *Accept(Visitor *v) override
    {
        return v->VisitNumberExpr(this);
    }

    static bool classof(const ASTNode *node) // 判断是否能进行指针的强转
    {
        return node->getKind() == ND_NumberExpr;
    }
public:
    int number;
};

// 变量访问节点
class VariableAccessExpr : public ASTNode // 变量表达式
{
public:
    VariableAccessExpr() : ASTNode(ND_VariableAccessExpr) {}

    llvm::Value *Accept(Visitor *v) override
    {
        return v->VisitVariableAccessExpr(this);
    }

    static bool classof(const ASTNode *node) // 判断是否能进行指针的强转
    {
        return node->getKind() == ND_VariableAccessExpr;
    }
public:
    llvm::StringRef name;
};

// 目标程序
class Program
{
public:
    std::vector<std::shared_ptr<ASTNode>> ExprVec;
};
